/**
 * 
 */
package org.statewalker.graph.util;

public class LevelBuilder {

    public interface IListener {

        void begin(int level, int[] depths);

        void beginLevel(int level, int[] depths);

        void end(int level, int[] depths);

        void endLevel(int level, int[] depths);
    }

    private int[] fDepths;

    private int fLevel = -1;

    private int[] fMaxDepths;

    private IListener fSectionListener;

    public LevelBuilder(IListener listener, int... maxDepths) {
        fSectionListener = listener;
        fMaxDepths = maxDepths;
        fDepths = new int[fMaxDepths.length];
    }

    private void begin(int level, int value) {
        while (fLevel < level) {
            fLevel++;
            fSectionListener.beginLevel(fLevel, fDepths);
        }
        beginLevel(value);
    }

    private void beginLevel(int value) {
        while (fDepths[fLevel] <= value) {
            fSectionListener.begin(fLevel, fDepths);
            if (fDepths[fLevel] == value)
                break;
            fDepths[fLevel]++;
        }
    }

    private void checkLevel(int level) {
        if (level < 0 || level >= fMaxDepths.length)
            throw new IllegalArgumentException("The level is out of range");
    }

    public void end() {
        end(-1, 0);
    }

    private void end(int level, int value) {
        while (fLevel > level) {
            endLevel(0);
            fSectionListener.endLevel(fLevel, fDepths);
            fLevel--;
        }
        if (fLevel == level)
            endLevel(value);
    }

    private void endLevel(int value) {
        if (fLevel < 0)
            return;
        while (fDepths[fLevel] >= value) {
            fSectionListener.end(fLevel, fDepths);
            if (fDepths[fLevel] == value)
                break;
            fDepths[fLevel]--;
        }
    }

    public int get(int level) {
        checkLevel(level);
        return fDepths[level];
    }

    public void set(int level, int value) {
        checkLevel(level);
        if (value < 0 || value >= fMaxDepths[level])
            throw new IllegalArgumentException("The value is out of range");
        end(level, value);
        begin(level, value);
    }
}